package com.myblog.demo.controller;

import com.myblog.demo.common.*;
import com.myblog.demo.entity.UserInfo;
import com.myblog.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @Autowired
    private CheckCodeTools codeTools;


    @Value("${photopath}")
    private String photoPath;

    // 锁对象
    private final Object locker = new Object();

    // 线程池对象
    private final ExecutorService service = Executors.newFixedThreadPool(ApplicationVariable.THREAD_NUMBER);

    /**
     * 用户注册
     *
     * @param userInfo 用户信息
     * @return 统一返回类型
     */
    @RequestMapping("/reg")
    public AjaxResult reg(UserInfo userInfo) {
        // 前端已经进行非空校验了，后端要不要进行非空校验
        // 铁律：后端开发，永远不要相信前端
        // 1. 参数合法性判断
        if (userInfo == null ||
                !StringUtils.hasLength(userInfo.getUsername()) ||
                !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(-1, "参数错误");
        }
        // 验证参数是否是数字或者字母
        if (!UserAuthenticationTools.isValidCredentials(userInfo.getUsername(), userInfo.getPassword())) {
            return AjaxResult.fail(-1, "用户名和密码只能是数字或字母！");
        }

        // 2. 对密码进行加盐加密
        userInfo.setPassword(PasswordTools.encrypt(userInfo.getPassword()));

        // 3. 调用 UserService 执行添加方法，并将返回的结果添加 AjaxResult.data 进行返回
        int result = userService.reg(userInfo);
        return AjaxResult.success(result);
    }

    /**
     * 用户登录操作，创建并储存 session
     *
     * @param username 用户名
     * @param password 密码
     * @param request  HttpServletRequest
     * @return 统一返回类型
     */
    @RequestMapping("/login")
    public AjaxResult login(String username, String password, String code, HttpServletRequest request) throws ParseException {
        // 1. 参数合法性判断
        if (!StringUtils.hasLength(username)
                || !StringUtils.hasLength(password)
                || !StringUtils.hasLength(code)) {
            return AjaxResult.fail(-1, "参数错误！");
        }

        /*
        增加白名单用户，用于后续测试，当 username、code 均为 admin，password 为 123456 的时候，直接登录成功
         */
        if (username.equals("admin") && password.equals("123456") && code.equals("admin")) {
            UserInfo adminUser = userService.login(username);
            HttpSession session = request.getSession();
            session.setAttribute(ApplicationVariable.SESSION_KEY_USERINFO, adminUser);
            return AjaxResult.success(1);
        }

        // 2. 验证
        // 2.1 用户名和密码只包含字母和数字

        if (!UserAuthenticationTools.isValidCredentials(username, password)) {
            return AjaxResult.fail(-1, "用户名和密码只能是数字或字母！");
        }

        // 2.2 验证码验证
        HttpSession session = request.getSession();
        String sessionCode = (String) session.getAttribute(ApplicationVariable.SESSION_KEY_CHECK_CODE);
        if (!code.equalsIgnoreCase(sessionCode)) {
            return AjaxResult.fail(-2, "验证码错误！");
        }


        // 3. 调用 UserService 执行登录查询操作，并根据返回的结果进行 AjaxResult 返回

        // 3.2 通过用户名获取数据库中的用户信息
        UserInfo loginUser = userService.login(username);
        if (loginUser == null || loginUser.getId() <= 0) {
            return AjaxResult.fail(-3, 0, "用户名或密码错误！");
        }

        // 3.2 对用户输错密码次数进行判断，如果测次数超过三次就对该用户进行冻结
        // 定义时间格式
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 当输入次数等于三的时候，对账户进行冻结
        if (!PasswordTools.decrypt(password, loginUser.getPassword())
                && userService.getStateByUsername(loginUser.getUsername()) == 3) {
            // 记录当前用户开始冻结的时间
            loginUser.setUpdatetime(LocalDateTime.now());
            // 持久化到数据库
            userService.updateUpdateTime(loginUser);
            // 使用多线程处理解冻时间
            /* Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (locker) {
                        try {
                            // state + 1
                            userService.addStateByUsername(loginUser.getUsername());
                            // 等待解冻
                            locker.wait(ApplicationVariable.FREEZING_TIME);
                            // 解冻完成后，恢复 state 为 0
                            userService.initStateByUsername(loginUser.getUsername());
                            // 唤醒
                            locker.notify();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });

            thread.start();*/
            // 使用线程池处理解冻时间
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        // state + 1
                        userService.addStateByUsername(loginUser.getUsername());

                        // 等待解冻
                        Thread.sleep(ApplicationVariable.FREEZING_TIME);

                        // 解冻完成后，恢复 state 为 0
                        userService.initStateByUsername(loginUser.getUsername());

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });

            return AjaxResult.fail(-1, "密码错误次数超过 3 次，请 " + ApplicationVariable.FREEZING_TIME / 1000 + "s 后重试！");

        } else if (userService.getStateByUsername(loginUser.getUsername()) > 3) {
            // 错误次数大于3的时候，计算剩余时间
            // 计算方法：剩余时间 = 开始冻结时间 + 冻结时间 - 当前时间
            // 获取开始冻结时间
            String[] times = loginUser.getUpdatetime().toString().split("T");
            Date date = simpleDateFormat.parse(times[0] + " " + times[1]);
            // 计算剩余时间，毫秒转成秒
            long leftTime = (date.getTime() + ApplicationVariable.FREEZING_TIME - System.currentTimeMillis()) / 1000;
            return AjaxResult.fail(-1, "账号已被冻结，请 " + leftTime + "s 后再试！");
        }

        // 3.3 对用户输入的Password进行判断
        if (!PasswordTools.decrypt(password, loginUser.getPassword())) {
            // 对该用户的 state 进行 +1 操作，表示输错密码次数加 1
            userService.addStateByUsername(loginUser.getUsername());
            return AjaxResult.fail(-3, 0, "用户名或密码错误！");
        }

        // 4. 将当前成功登录的用户信息储存到 session 中
        session.setAttribute(ApplicationVariable.SESSION_KEY_USERINFO, loginUser);

        // 5. 登录成功的时候，恢复该用户的 state
        userService.initStateByUsername(loginUser.getUsername());
        return AjaxResult.success(1);
    }

    /**
     * 退出登录
     *
     * @param request HttpServletRequest
     * @return 统一返回类型
     */
    @RequestMapping("/logout")
    public AjaxResult logout(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute(ApplicationVariable.SESSION_KEY_USERINFO) == null) {
            return AjaxResult.fail(-3, "当前用户未登录！");
        }

        // 移除当前用户的 session
        session.removeAttribute(ApplicationVariable.SESSION_KEY_USERINFO);
        return AjaxResult.success(1);
    }

    /**
     * 判断当前用户是否登录
     *
     * @param request HttpServletRequest
     * @return 统一类型返回，data为 0 表示未登录，为 1 表示已登录。
     */
    @RequestMapping("/islogin")
    public AjaxResult isLogin(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (UserSessionTools.getLoginUser(request) == null) {
            return AjaxResult.success(0);
        }
        return AjaxResult.success(1);
    }


    /**
     * 获取验证码
     *
     * @param request HttpServletRequest
     * @return 统一类型返回
     */
    @RequestMapping("/getcode")
    public AjaxResult getCode(HttpServletRequest request) {
        // 创建图片验证码
        String[] codeArr = codeTools.createImage();
        // 将验证码储存到 session
        HttpSession session = request.getSession();
        session.setAttribute(ApplicationVariable.SESSION_KEY_CHECK_CODE, codeArr[1]);
        // 返回图片地址
        return AjaxResult.success("/image/" + codeArr[0]);
    }

    /**
     * 获取登录的用户信息
     *
     * @param request HttpServletRequest
     * @return AjaxResult
     */
    @RequestMapping("/getuserinfo")
    public AjaxResult getUserInfo(HttpServletRequest request) {
        UserInfo loginUser = UserSessionTools.getLoginUser(request);
        if (loginUser == null) {
            return AjaxResult.fail(-1, "当前用户未登录！");
        }

        int id = loginUser.getId();
        UserInfo userInfo = userService.getUserInfoById(id);
        // 清除密码
        userInfo.setPassword("");

        return AjaxResult.success(userInfo);
    }

    /**
     * 根据用户id获取用户信息
     *
     * @param uid 用户id
     * @return AjaxResult
     */
    @RequestMapping("/getuserinfobyuid")
    public AjaxResult getUserInfoByUId(Integer uid) {

        if (uid == null || uid <= 0) {
            return AjaxResult.fail(-1, "参数错误！");
        }

        UserInfo userInfo = userService.getUserInfoById(uid);
        // 清除密码
        userInfo.setPassword("");

        return AjaxResult.success(userInfo);
    }

    /**
     * 保存用户上传的头像图片
     *
     * @param file    MultipartFile
     * @param request HttpServletRequest
     * @return AjaxResult
     */
    @RequestMapping("/uploadphoto")
    public AjaxResult uploadPhoto(@RequestPart("avatarFile") MultipartFile file, HttpServletRequest request) {
        if (file == null) {
            return AjaxResult.fail(-1, "上传图片失败！");
        }

        if (!PhotoTools.isImage(file)) {
            return AjaxResult.fail(-1, "图片格式错误！");
        }

        if (!PhotoTools.isImageSizeValid(file, 5 * 1024 * 1024)) {
            return AjaxResult.fail(-1, "图片大小超出限制5MB！");
        }

        // 生成并获取唯一图片文件名
        String fileName = PhotoTools.generateUniqueFileName(file);

        // 保存文件
        if (!PhotoTools.saveImage(file, photoPath, fileName)) {
            return AjaxResult.fail(-1, "保存图片失败！");
        }

        // 将文件名保存到数据库中
        UserInfo loginUser = UserSessionTools.getLoginUser(request);
        if (loginUser == null) {
            return AjaxResult.fail(-1, "当前用户未登录！");
        }
        int result = userService.savePhotoPath(fileName, loginUser.getId());

        System.out.println(photoPath);
        return AjaxResult.success(result);
    }

    /**
     * 修改用户昵称
     *
     * @param nickname 昵称
     * @param request  HttpServletRequest
     * @return AjaxResult
     */
    @RequestMapping("/subnickname")
    public AjaxResult updateNickname(String nickname, HttpServletRequest request) {
        // 参数校验
        if (!StringUtils.hasLength(nickname)) {
            return AjaxResult.fail(-1, "参数错误！");
        }

        // 获取当前登录用户
        UserInfo loginUser = UserSessionTools.getLoginUser(request);
        if (loginUser == null) {
            return AjaxResult.fail(-1, "当前用户未登录！");
        }

        int id = loginUser.getId();
        int result = userService.updateNickname(nickname, id);
        return AjaxResult.success(result);
    }

    /**
     * 修改 gitee 地址
     *
     * @param gitee   url地址
     * @param request HttpServletRequest
     * @return AjaxResult
     */
    @RequestMapping("/subgitee")
    public AjaxResult updateGitee(String gitee, HttpServletRequest request) {
        // 参数校验
        if (!StringUtils.hasLength(gitee)) {
            return AjaxResult.fail(-1, "参数错误！");
        }

        // 获取当前登录用户
        UserInfo loginUser = UserSessionTools.getLoginUser(request);
        if (loginUser == null) {
            return AjaxResult.fail(-1, "当前用户未登录！");
        }

        int id = loginUser.getId();
        int result = userService.updateGitee(gitee, id);
        return AjaxResult.success(result);
    }

    /**
     * 用于获取指定用户的总阅读量和获评数
     *
     * @param uid     用户id
     * @param request HttpServletRequest
     * @return AjaxResult
     */
    @RequestMapping("/get_total_rcount_and_comment")
    public AjaxResult getTotalRCountAndCommentByUid(Integer uid, HttpServletRequest request) {
        if (uid == null || uid < 0) {
            return AjaxResult.fail(-1, "参数错误");
        }

        HashMap<String, Integer> map = userService.getTotalRCountAndCommentByUid(uid);
        return AjaxResult.success(map);
    }

    /**
     * 判断uid用户是否是当前登录用户
     *
     * @param uid     要判断的用户id
     * @param request HttpServletRequest
     * @return AjaxResult
     */
    @RequestMapping("/is_login_user")
    public AjaxResult isLoginUser(Integer uid, HttpServletRequest request) {
        if (uid == null || uid <= 0) {
            return AjaxResult.fail(-1, "参数错误！");
        }
        // 获取当前登录用户信息
        UserInfo loginUser = UserSessionTools.getLoginUser(request);
        if (loginUser == null) {
            // 当前用户未登录
            return AjaxResult.success(0);
        }

        int loginUserId = loginUser.getId();

        // 判断uid是否为当前登录用户
        if (uid == loginUserId) {
            // 是当前登录用户
            return AjaxResult.success(1);
        } else {
            // 不是当前登录用户
            return AjaxResult.success(-1);
        }
    }
}
